#ifndef _RESIDUE_H
#define _RESIDUE_H
#include "../bulk_storage/bulk_storage.h"
#include "atom.h"
#include "../metric/xyz.h"

#include <gsl/gsl_vector.h>

#include <assert.h>

/** \defgroup AAStruct Amino Acid representations

    \ingroup AA
*/

/** \addtogroup res Amino Acid Residues (Fixed Width)
 * DOXYGEN HATES ME!
 *  \ingroup AAStruct
 */

/** \addtogroup memres Amino Acid Residues (Variable)
 *  \ingroup AAStruct
 */

/** 
 * Fixed width aminio acid structure for writing to disk in 
 * bulk_store, mTree.
 * \ingroup AAStruct
 */
struct residue{
  /** Residue type abbrev. in aa_table.  */
  char residue_type;

  /** First atom number.  */
  off_t startA;

  /** Last atom number.  */
  off_t stopA;

  /** Alpha carbon atom number.  */
  /*  off_t CA; */
};

/** 
 * Variable sized structre for use in memory when computing distances. 
 * \ingroup AAStruct
 */
struct mem_res{
  /** The residue object this is based on.  */
  struct residue res;

  /** Alpha carbon.  */
  struct atom* ca;

  /** Atoms in this residue.  */
  struct atom** atoms;

  /** Number of atoms in atoms  */
  size_t n;

  /** Coordinates of functional group.  */
  gsl_vector* fg;
};


/** 
 * \brief Pretty print the residue.
 * 
 * \param r - The residue to print; 
 * 
 * \ingroup memres
 */
void mem_res_print (const struct mem_res* r);


/** 
 * \brief Insert an atom into the residue.
 * 
 * \param r - The residue to modify.
 * \param a - The atom to insert.
 *
 * \ingroup memres
 */
void mem_res_add_atom(struct mem_res* r, const struct atom* a);


/** 
 * Load a mem_res structure from atom store on disk.
 * 
 * \param mr - Pointer to the mem_res to create.
 * \param r - Pointer to the residue to base the mem_res on.
 * \param s - Atom store to use.
 *
 * \ingroup memres  
 */
void mem_res_load(struct mem_res* mr, const struct residue* r, const struct store* s);


/** 
 * \brief Effective distance between two residues.
 *
 * Distance is computed as \f$ min \{min_i d(r_0.a_s, r_1.a_i), min_i d(r_0.a_i, r_1.a_s)\} $\f .
 *
 * \ingroup res
 */
double mem_res_min_effect_dist(const struct mem_res* r0, const struct mem_res* r1);


/** 
 * \brief Lookup functional group (atom) in aa_table
 * 
 * \param mr - In-memory residue to lookup
 * 
 * \return A pointer to the atom in mr that is 
 * listed as the functional group.
 * \ingroup memres
 */
struct atom* get_functional_group(const struct mem_res* mr);

/** 
 * Just a callback for qsort in the distance function.
 * 
 * \return -1 less than. / 0 equal to / +1 greater than 
 */
int compdbl(const void* d0, const void* d1);



/** 
* \brief Effective distance between two residues.
 *
 * Distance is computed as \f$ min \{min_i d(r_0.a_s, r_1.a_i), min_i d(r_0.a_i, r_1.a_s)\} $\f .
 *
 * \ingroup res
 */
double residue_min_effect_dist(const struct residue* r0, 
			       const struct residue* r1, 
			       const struct store* s);


/** 
 * \brief Attempt to locate the functional group. 
 *
 * If no functional group is listed in the aa_table, 
 * the sidechain mass center is used.
 * 
 * \param mr - Pointer to residue to operate on.
 * \param asa0 - Pointer to first atom in functional group.
 * \param asa1 - Pointer to second atom in functional group.
 * \ingroup memres
 */
void mem_res_compute_functional_group(const struct mem_res* mr,
				      const struct atom* asa0,
				      const struct atom* asa1);


/** 
 * \brief atom -> vector conversion.
 *
 * Does element-wise copy from atom coordinates to vector.
 * 
 * \param v - Pointer to the vector to copy to.
 * \param a - Pointer to the atom to copy from.
 * \ingroup AAStruct
 */
void atom2vd(gsl_vector* v, const struct atom* a);


/** 
 * \brief Compute the center of mass of atoms in ag.
 *
 * \param v - Vector where the result goes.
 * \param ag - Array of atom pointers.
 * \param n - Size of ag.
 * \ingroup AAStruct
 */
void mass_center(gsl_vector* v, const struct atom** ag, size_t n);


/** 
 * \brief Find sidechain atoms.
 *
 * Constructs an array of pointers in ag to the sidechain atoms in mr.
 * 
 * \param mr - The residue to operate on.
 * \param ag - The array.
 * \param n - Number of atoms in the sidechain.
 * \ingroup memres
 */
void mem_res_side_chain(const struct mem_res* mr, struct atom*** ag, size_t* n);

#endif //_RESIDUE_H






